Summary
PHASE7 has created cluster-based graph -- with traversal cluster as node and traversal cluster links as edges.
Using previous phases, this phase accomplishes following:
STEP12i: Identifying waterway geometetries
India Major Rivers (When coming from North-to-South):
Ravi River: Kangra to Harike Lake
Beas River: Chamba to Kathlaur
Sutlej River: Rekong Peo to Firozpur
Yamuna River: From Himalya to Prayagraj
Ganga River: From Himalya to Bay of Bengal
Narmada River: From Amarkantak to Arabic Sea
Mahanadhi River: From Dudwa to Pardip
Godavari River: From Trimbak to Mummidivaram
Krishna River: From Mahabaleshwar to Machilipatnam
Kaveri River: Talakaveri to Kaveripattinam
Kandaleru Reservoir: Source of Telugu-Ganga Kandaleru-Poondi Canal
Poondi Reservoir: Destination of Telugu-Ganga Kandaleru-Poondi Canal
NOTE:
This and next two way-to-way steps are completed at this stage -- so that any on-the-fly traversal cross-link generated will be accounted in delooping/intesections.
Getting OpenStreetMap Relation-Ids for Rivers:
Ref: https://wiki.openstreetmap.org/wiki/Overpass_turbo/Wizard
https://gis.stackexchange.com/questions/98371/getting-shapefile-of-river-from-openstreetmap
<relation id="1236345" <tag k="destination" v="Indian Ocean"/> <tag k="name" v="Ganga"/> <tag k="type" v="waterway"/> <tag k="waterway" v="river"/>
<relation id="1236345"
<tag k="destination" v="Indian Ocean"/>
<tag k="name" v="Ganga"/>
<tag k="type" v="waterway"/>
<tag k="waterway" v="river"/>NOTE:
The "member" entries in file are sub-elements. Only the "tag" entries are relevent to us
For way based link (e-g., https://www.openstreetmap.org/way/164799202):
Some waterways -- such as reservoir, may be identified as "way" -- not as "relation"
In this case the xml content will look like:
<way id="164799202" visible="true" version="9" changeset="168221120" timestamp="2025-06-28T10:31:50Z" user="vaibhavnz" uid="4164103"> <nd ref="1764080025"/> <nd ref="1764080028"/>
<way id="164799202" visible="true" version="9" changeset="168221120" timestamp="2025-06-28T10:31:50Z" user="vaibhavnz" uid="4164103">
<nd ref="1764080025"/>
<nd ref="1764080028"/>The same way, extract relation/way ids and download xml file for all other rivers and reservoirs
Downloading Shape-file for Relation:
Go to: https://overpass-turbo.eu/
Pan to India
WARNING If you didn't pan to India, the subsequent queries would be scoped to current view and would fail
Click Wizard
Query:
type:relation and id:164799202
Or (slower query):
boundary=administrative and type=boundary and name="Mainland India"
Build and Run Query
<<runs for long time >>
Export
Data | GeoJSON : C:\GangaToKaveri\qgis\osm_turbo_india_geom_164799202_export.geojson
NOTE:
For "way" based waterway -- such as reservoir, use the query:
type:way and id:164799202
Click Wizard
Query:
name="Ganga" and waterway=river and type=waterway and destination="Indian Ocean"
Or:
type:relation and id:1236345
Build and Run Query
Export
As GeoJSON: C:\GangaToKaveri\qgis\osm_turbo_ganges_geom_1236345_export.geojson
Same way, download geojson files for all other relations/ways
STEP12j: Loading geojson file to database (populating way_geometry, and way_geometry_points)
Loading India Boundary Scope -- populating the g2k_graph_scope_boundary table
NOTE: India Boundary Scope will be checked to include the way geometry -- to avoid way-to-way solutions starting or ending outside India
COPY %GEO_FILE% %GEO_FILE%.orig
EDIT %GEO_FILE%
Remove ALL '"type": "Feature"' blocks containing geometry of type OTHER THAN Polygon
E-g., blocks to remove:
{
"type": "Feature",
"properties": {
"@id": "node/16173236",
"@relations": [
{
"role": "admin_centre",
"rel": 17453159,
"reltags": {
"boundary": "administrative",
"name": "Mainland India",
"type": "boundary"
}
}
]
},
"geometry": {
"type": "Point",
"coordinates": [
77.2090057,
28.6138954
]
},
"id": "node/16173236"
}, {
"type": "Feature",
"properties": {
"@id": "node/16173236",
"@relations": [
{
"role": "admin_centre",
"rel": 17453159,
"reltags": {
"boundary": "administrative",
"name": "Mainland India",
"type": "boundary"
}
}
]
},
"geometry": {
"type": "Point",
"coordinates": [
77.2090057,
28.6138954
]
},
"id": "node/16173236"
},NOTE: The non-Polygon geometries need to be removed to contain ONLY Polygon geometry
vi ${PCH_APP_HOME}/run_G2KWayGeometryLoader.shSET WAY_TITLE='Ganga River'
SET WAY_SUB_TITLE='From Himalya to Bay of Bengal'
SET GEO_FILE=${G2K_HOME}/qgis/osm_turbo_ganges_geom_1236345_export.geojsonSET WAY_TITLE='Ganga River'
SET WAY_SUB_TITLE='From Himalya to Bay of Bengal'
SET GEO_FILE=${G2K_HOME}/qgis/osm_turbo_ganges_geom_1236345_export.geojson${PCH_APP_HOME}/run_G2KWayGeometryLoader.sh
Same way load all other geojson files for other rivers/reservoirs,
Below picture shows example waterway geometries of Godavari and Krishna rivers

Ref: C:\GangaToKaveri\docs\godhakrishnablank.png
Analyze way_geometry_points Counts:
SELECT src_geom.title, src_geom.subtitle, COUNT(src_pts.way_pt_id) AS total_way_points
FROM way_geometry_points AS src_pts
INNER JOIN way_geometry AS src_geom ON src_pts.way_id=src_geom.way_id
GROUP BY src_geom.title, src_geom.subtitle;
Results:
title | subtitle | total_way_points
----------------+----------------------------------------------+------------------
Ganga River | From Himalya to Bay of Bengal | 5276
Godavari River | From Trimbak to Mummidivaram | 9138
Kaveri River | Talakaveri to Kaveripattinam | 4914
Krishna River | From Mahabaleshwar to Machilipatnam | 7888
Narmada River | From Amarkantak to Arabic Sea | 8041
Yamuna River | From Himalya to Prayagraj | 7598
Results:
title | subtitle | total_way_points
----------------+----------------------------------------------+------------------
Ganga River | From Himalya to Bay of Bengal | 5276
Godavari River | From Trimbak to Mummidivaram | 9138
Kaveri River | Talakaveri to Kaveripattinam | 4914
Krishna River | From Mahabaleshwar to Machilipatnam | 7888
Narmada River | From Amarkantak to Arabic Sea | 8041
Yamuna River | From Himalya to Prayagraj | 7598
WITH point_counts AS (
SELECT src_geom.title, src_geom.subtitle, COUNT(src.way_pt_id) AS total_points
FROM way_geometry_points AS src, way_geometry AS src_geom
WHERE src.way_id=src_geom.way_id
GROUP BY src_geom.title, src_geom.subtitle
)
SELECT title, subtitle, total_points, CEIL((CEIL(total_points/100.0)*10000)/800)*800 AS points_to_search
FROM point_counts;
NOTE:
800 is the fetchBatchSize used below for run_G2KWayToWaySolutionBuilder
The total_points below is used as rowEndAt in calls to run_G2KWayToWaySolutionBuilder
title | subtitle | total_points | points_to_search | With G2Y-and-B2Y ----------------+-------------------------------------+--------------+----------------------+--------------------- Ganga River | From Himalya to Bay of Bengal | 5276 | 530400 | 8140 | 820000 Yamuna River | From Himalya to Prayagraj | 7598 | 760000 | 9881 | 990400 Narmada River | From Amarkantak to Arabic Sea | 8041 | 810400 Godavari River | From Trimbak to Mummidivaram | 9138 | 920000 Krishna River | From Mahabaleshwar to Machilipatnam | 7888 | 790400 Kaveri River | Talakaveri to Kaveripattinam | 4914 | 500000 Kandaleru Reservoir | Source of Telugu-Ganga Kandaleru-Poondi Canal | 1467 | 150400 Poondi Reservoir | Destination of Telugu-Ganga Kandaleru-Poondi Canal | 681 | 70400
title | subtitle | total_points | points_to_search | With G2Y-and-B2Y
----------------+-------------------------------------+--------------+----------------------+---------------------
Ganga River | From Himalya to Bay of Bengal | 5276 | 530400 | 8140 | 820000
Yamuna River | From Himalya to Prayagraj | 7598 | 760000 | 9881 | 990400
Narmada River | From Amarkantak to Arabic Sea | 8041 | 810400
Godavari River | From Trimbak to Mummidivaram | 9138 | 920000
Krishna River | From Mahabaleshwar to Machilipatnam | 7888 | 790400
Kaveri River | Talakaveri to Kaveripattinam | 4914 | 500000
Kandaleru Reservoir | Source of Telugu-Ganga Kandaleru-Poondi Canal | 1467 | 150400
Poondi Reservoir | Destination of Telugu-Ganga Kandaleru-Poondi Canal | 681 | 70400NOTE:
To use points_to_search of "With G2Y-and-B2Y" in run_G2KWayToWaySolutionBuilder for wayToWayBuildSolutions, pass the "--useOnlyTitles true --yesIReallyWantToDo true"
Config Params:
fetchBatchSize can be large (set to 800)
Keep wayToWayNearestClustersForShortestPaths (5) matching to G2K Routing REST Server's "g2krouting.nearestNodesForShortestPaths" entry
wayToWayMaxDstPointsPerSrcPoint=100 means that for each source point 100 nearby destination points will be pathed and solutions saved.
onTheFlyDistanceCapKm is for online service to cap how far-away nodes missing the links will be linked on-the-fly at runtime
It is set to more than 5 times online service value (3.0) -- to link far-away nodes in this offline step
On Linux:
vi ${PCH_APP_HOME}/run_G2KWayToWaySolutionBuilder.sh
SRC_WAY_TITLE='Ganga River' SRC_WAY_SUB_TITLE='From Himalya to Bay of Bengal' DST_WAY_TITLE='Yamuna River' DST_WAY_SUB_TITLE='From Himalya to Prayagraj'
SRC_WAY_TITLE='Ganga River'
SRC_WAY_SUB_TITLE='From Himalya to Bay of Bengal'
DST_WAY_TITLE='Yamuna River'
DST_WAY_SUB_TITLE='From Himalya to Prayagraj'WARNING Make sure to set title variables wrapped around single-quote -- to get passed correctly to Java program
nohup ${PCH_APP_HOME}/run_G2KWayToWaySolutionBuilder.sh --action wayToWayBuildSolutions --rowBeginAt=0 --rowEndAt=530400 > $PCH_LOG_HOME/nohup.out 2>&1 &
The same way, call for all other different source and destination rivers.
SELECT COUNT(*) FROM way_to_way_solutions;
Results: count -------- 145725
Results:
count
--------
145725
~~~~~ Take incremental DB backup ~~~~~
STEP12l: Removing loops in traversal intra-cluster links (populating the table: traversal_cluster_xlnk_deloop_link_highorder2, and adding/updating node_highorder2,edge_highorder2)
This step goes through complete and partial solutions in traversal intra-cluster links in traversal_cluster_cross_links_highorder2 table and identifies geographical loops and removes them
Example of geographical loops over graphical routing:

Ref: C:\GangaToKaveri\docs\qwc_routing_with_loops_highlighted.png
Run below in six parallel pgsql shells:
SET statement_timeout = 19200000; SET lock_timeout=1200000; SET deadlock_timeout = 12000; SET work_mem TO '2048MB';
SELECT 250 AS batch_size, 500 AS max_rounds, 1 AS start_round_num, -1 AS starting_link_edge_id, 63021110 AS ending_link_edge_id \gset SELECT 250 AS batch_size, 500 AS max_rounds, 1 AS start_round_num, 63021110 AS starting_link_edge_id, 78516516 AS ending_link_edge_id \gset SELECT 250 AS batch_size, 500 AS max_rounds, 1 AS start_round_num, 78516516 AS starting_link_edge_id, 94011922 AS ending_link_edge_id \gset SELECT 250 AS batch_size, 500 AS max_rounds, 1 AS start_round_num, 94011922 AS starting_link_edge_id, -1 AS ending_link_edge_id \gset CALL g2k_run_remove_traversal_cluster_cross_link_loops_highorder2(:batch_size, :max_rounds::smallint, :start_round_num::smallint, :starting_link_edge_id::bigint, :ending_link_edge_id::bigint);
SELECT 250 AS batch_size, 500 AS max_rounds, 1 AS start_round_num, -1 AS starting_link_edge_id, 63021110 AS ending_link_edge_id \gset
SELECT 250 AS batch_size, 500 AS max_rounds, 1 AS start_round_num, 63021110 AS starting_link_edge_id, 78516516 AS ending_link_edge_id \gset
SELECT 250 AS batch_size, 500 AS max_rounds, 1 AS start_round_num, 78516516 AS starting_link_edge_id, 94011922 AS ending_link_edge_id \gset
SELECT 250 AS batch_size, 500 AS max_rounds, 1 AS start_round_num, 94011922 AS starting_link_edge_id, -1 AS ending_link_edge_id \gset
CALL g2k_run_remove_traversal_cluster_cross_link_loops_highorder2(:batch_size, :max_rounds::smallint, :start_round_num::smallint, :starting_link_edge_id::bigint, :ending_link_edge_id::bigint); Call for each range
NOTE:
You may start few sessions (4) and parallelize the calls
Removes ~90000 loops per 20 percentile batch
Removes loops 2 percentile at a time (per transaction), in 20 percentile batches -- completing 100 percentile in 360min
Maximum rounds run for each 2 percentile was < 10
Analyze way-to-way solutions:
SELECT 'Ganga River' AS src_way_title, 'From Himalya to Bay of Bengal' AS src_way_sub_title, 'Kaveri River' AS src_way_title, 'Talakaveri to Kaveripattinam' AS src_way_sub_title \gset
SELECT 79.9776 AS dest_lon, 27.0246 AS dest_lat \gset
SELECT 'Kaveri River' AS src_way_title, 'Talakaveri to Kaveripattinam' AS src_way_sub_title, 'Ganga River' AS dst_way_title, 'From Himalya to Bay of Bengal' AS dst_way_sub_title \gset
SELECT 77.7766 AS dest_lon, 12.1203 AS dest_lat \gset
WITH from_pt_ids AS (
SELECT src.way_pt_id AS src_way_pt_id, ST_AsText(src.way_pt) AS src_pt, ST_SetSRID(ST_MakePoint(:dest_lon, :dest_lat), 4326)::geometry <-> src.way_pt AS src_pt_dist
FROM way_geometry_points AS src, way_geometry AS src_geom
WHERE 1=1
AND src.way_id=src_geom.way_id
AND src_geom.title=:dst_way_title AND src_geom.subtitle=:dst_way_sub_title
ORDER BY ST_SetSRID(ST_MakePoint(:dest_lon, :dest_lat), 4326)::geometry <-> src.way_pt
LIMIT 100
), to_pt_ids AS (
SELECT dst.way_pt_id AS dst_way_pt_id
FROM way_geometry_points AS dst, way_geometry AS dst_geom
WHERE 1=1
AND dst.way_id=dst_geom.way_id
AND dst_geom.title=:src_way_title AND dst_geom.subtitle=:src_way_sub_title
), from_to_pts AS (
SELECT src.src_way_pt_id, dst.dst_way_pt_id
FROM from_pt_ids AS src CROSS JOIN to_pt_ids AS dst
), solns AS (
SELECT wws.solution_id,wws.status,wws.edge_cost,wws.distance,wws.src_way_pt_id,wws.dst_way_pt_id,
ROW_NUMBER() OVER (PARTITION BY wws.edge_cost,wws.distance) AS rownum
FROM way_to_way_solutions AS wws
INNER JOIN from_to_pts ftp ON (wws.src_way_pt_id=ftp.src_way_pt_id AND wws.dst_way_pt_id=ftp.dst_way_pt_id)
WHERE wws.status = 0
)
SELECT solns.solution_id,solns.status,solns.edge_cost,solns.distance,solns.src_way_pt_id,solns.dst_way_pt_id
FROM solns
WHERE solns.rownum=1
ORDER BY solns.edge_cost LIMIT 10;
~~~~~ Take incremental DB backup ~~~~~
The result of this phase is pre-calculated cost-effective paths between rivers that are long distance apart.
Below picture is the example of cost-effective paths researched by G2K from Ganga river to Kaveri river:

Ref: C:\GangaToKaveri\docs\g2k_ganga_to_kaveri_route_05132025.png